<template>
  <view class="uni-file-picker">
    <view v-if="title" class="uni-file-picker__header">
      <text class="file-title">{{ title }}</text>
      <text class="file-count">{{ filesList.length }}/{{ limitLength }}</text>
    </view>
    <view v-if="subtitle" class="file-subtitle">
      <view>{{ subtitle }}</view>
    </view>
    <upload-image
        v-if="fileMediatype === 'image' && showType === 'grid'"
        :readonly="readonly"
        :image-styles="imageStyles"
        :files-list="url"
        :limit="limitLength"
        :disablePreview="disablePreview"
        :delIcon="delIcon"
        @uploadFiles="uploadFiles"
        @choose="choose"
        @delFile="delFile"
    >
      <slot>
        <view class="is-add">
          <image :src="imgsrc" class="add-icon"></image>
        </view>
      </slot>
    </upload-image>
    <upload-file
        v-if="fileMediatype !== 'image' || showType !== 'grid'"
        :readonly="readonly"
        :list-styles="listStyles"
        :files-list="filesList"
        :showType="showType"
        :delIcon="delIcon"
        @uploadFiles="uploadFiles"
        @choose="choose"
        @delFile="delFile"
    >
      <slot><button type="primary" size="mini">选择文件</button></slot>
    </upload-file>
  </view>
</template>

<script>
import { chooseAndUploadFile, uploadCloudFiles } from './choose-and-upload-file.js';
import {
  get_file_ext,
  get_extname,
  get_files_and_is_max,
  get_file_info,
  get_file_data,
} from './utils.js';
import uploadImage from './upload-image.vue';
import uploadFile from './upload-file.vue';
import sheep from '@/sheep';
let fileInput = null;
/**
 * FilePicker 文件选择上传
 * @description 文件选择上传组件，可以选择图片、视频等任意文件并上传到当前绑定的服务空间
 * @tutorial https://ext.dcloud.net.cn/plugin?id=4079
 * @property {Object|Array}	value	组件数据，通常用来回显 ,类型由return-type属性决定
 * @property {String|Array}	url	  url数据
 * @property {Boolean}	disabled = [true|false]	组件禁用
 * 	@value true 	禁用
 * 	@value false 	取消禁用
 * @property {Boolean}	readonly = [true|false]	组件只读，不可选择，不显示进度，不显示删除按钮
 * 	@value true 	只读
 * 	@value false 	取消只读
 * @property {Boolean}	disable-preview = [true|false]	禁用图片预览，仅 mode:grid 时生效
 * 	@value true 	禁用图片预览
 * 	@value false 	取消禁用图片预览
 * @property {Boolean}	del-icon = [true|false]	是否显示删除按钮
 * 	@value true 	显示删除按钮
 * 	@value false 	不显示删除按钮
 * @property {Boolean}	auto-upload = [true|false]	是否自动上传，值为true则只触发@select,可自行上传
 * 	@value true 	自动上传
 * 	@value false 	取消自动上传
 * @property {Number|String}	limit	最大选择个数 ，h5 会自动忽略多选的部分
 * @property {String}	title	组件标题，右侧显示上传计数
 * @property {String}	mode = [list|grid]	选择文件后的文件列表样式
 * 	@value list 	列表显示
 * 	@value grid 	宫格显示
 * @property {String}	file-mediatype = [image|video|all]	选择文件类型
 * 	@value image	只选择图片
 * 	@value video	只选择视频
 * 	@value all		选择所有文件
 * @property {Array}	file-extname	选择文件后缀，根据 file-mediatype 属性而不同
 * @property {Object}	list-style	mode:list 时的样式
 * @property {Object}	image-styles	选择文件后缀，根据 file-mediatype 属性而不同
 * @event {Function} select 	选择文件后触发
 * @event {Function} progress 文件上传时触发
 * @event {Function} success 	上传成功触发
 * @event {Function} fail 		上传失败触发
 * @event {Function} delete 	文件从列表移除时触发
 */
export default {
  name: 'sUploader',
  components: {
    uploadImage,
    uploadFile,
  },
  options: {
    virtualHost: true,
  },
  emits: ['select', 'success', 'fail', 'progress', 'delete', 'update:modelValue', 'update:url'],
  props: {
    modelValue: {
      type: [Array, Object],
      default() {
        return [];
      },
    },
    url: {
      type: [Array, String],
      default() {
        return [];
      },
    },
    disabled: {
      type: Boolean,
      default: false,
    },
    disablePreview: {
      type: Boolean,
      default: false,
    },
    delIcon: {
      type: Boolean,
      default: true,
    },
    // 自动上传
    autoUpload: {
      type: Boolean,
      default: true,
    },
    // 最大选择个数 ，h5只能限制单选或是多选
    limit: {
      type: [Number, String],
      default: 9,
    },
    // 列表样式 grid | list | list-card
    mode: {
      type: String,
      default: 'grid',
    },
    // 选择文件类型  image/video/all
    fileMediatype: {
      type: String,
      default: 'image',
    },
    // 文件类型筛选
    fileExtname: {
      type: [Array, String],
      default() {
        return [];
      },
    },
    title: {
      type: String,
      default: '',
    },
    listStyles: {
      type: Object,
      default() {
        return {
          // 是否显示边框
          border: true,
          // 是否显示分隔线
          dividline: true,
          // 线条样式
          borderStyle: {},
        };
      },
    },
    imageStyles: {
      type: Object,
      default() {
        return {
          width: 'auto',
          height: 'auto',
        };
      },
    },
    readonly: {
      type: Boolean,
      default: false,
    },
    sizeType: {
      type: Array,
      default() {
        return ['original', 'compressed'];
      },
    },
    driver: {
      type: String,
      default: 'local', // local=本地 | oss | unicloud
    },
    subtitle: {
      type: String,
      default: '',
    },
  },
  data() {
    return {
      files: [],
      localValue: [],
      imgsrc: '/static/upload-camera.png',
    };
  },
  watch: {
    modelValue: {
      handler(newVal, oldVal) {
        this.setValue(newVal, oldVal);
      },
      immediate: true,
    },
  },
  computed: {
    returnType() {
      if (this.limit > 1) {
        return 'array';
      }
      return 'object';
    },
    filesList() {
      let files = [];
      this.files.forEach((v) => {
        files.push(v);
      });
      return files;
    },
    showType() {
      if (this.fileMediatype === 'image') {
        return this.mode;
      }
      return 'list';
    },
    limitLength() {
      if (this.returnType === 'object') {
        return 1;
      }
      if (!this.limit) {
        return 1;
      }
      if (this.limit >= 9) {
        return 9;
      }
      return this.limit;
    },
  },
  created() {
    if (this.driver === 'local') {
      uniCloud.chooseAndUploadFile = chooseAndUploadFile;
    }
    this.form = this.getForm('uniForms');
    this.formItem = this.getForm('uniFormsItem');
    if (this.form && this.formItem) {
      if (this.formItem.name) {
        this.rename = this.formItem.name;
        this.form.inputChildrens.push(this);
      }
    }
  },
  methods: {
    /**
     * 公开用户使用，清空文件
     * @param {Object} index
     */
    clearFiles(index) {
      if (index !== 0 && !index) {
        this.files = [];
        this.$nextTick(() => {
          this.setEmit();
        });
      } else {
        this.files.splice(index, 1);
      }
      this.$nextTick(() => {
        this.setEmit();
      });
    },
    /**
     * 公开用户使用，继续上传
     */
    upload() {
      let files = [];
      this.files.forEach((v, index) => {
        if (v.status === 'ready' || v.status === 'error') {
          files.push(Object.assign({}, v));
        }
      });
      return this.uploadFiles(files);
    },
    async setValue(newVal, oldVal) {
      const newData = async (v) => {
        const reg = /cloud:\/\/([\w.]+\/?)\S*/;
        let url = '';
        if (v.fileID) {
          url = v.fileID;
        } else {
          url = v.url;
        }
        if (reg.test(url)) {
          v.fileID = url;
          v.url = await this.getTempFileURL(url);
        }
        if (v.url) v.path = v.url;
        return v;
      };
      if (this.returnType === 'object') {
        if (newVal) {
          await newData(newVal);
        } else {
          newVal = {};
        }
      } else {
        if (!newVal) newVal = [];
        for (let i = 0; i < newVal.length; i++) {
          let v = newVal[i];
          await newData(v);
        }
      }
      this.localValue = newVal;
      if (this.form && this.formItem && !this.is_reset) {
        this.is_reset = false;
        this.formItem.setValue(this.localValue);
      }
      let filesData = Object.keys(newVal).length > 0 ? newVal : [];
      this.files = [].concat(filesData);
    },

    /**
     * 选择文件
     */
    choose() {
      if (this.disabled) return;
      if (
          this.files.length >= Number(this.limitLength) &&
          this.showType !== 'grid' &&
          this.returnType === 'array'
      ) {
        uni.showToast({
          title: `您最多选择 ${this.limitLength} 个文件`,
          icon: 'none',
        });
        return;
      }
      this.chooseFiles();
    },

    /**
     * 选择文件并上传
     */
    chooseFiles() {
      const _extname = get_extname(this.fileExtname);
      uni.setStorageSync('appOnNotShow',true)
      // 获取后缀
      uniCloud
          .chooseAndUploadFile({
            type: this.fileMediatype,
            compressed: false,
            sizeType: this.sizeType,
            // TODO 如果为空，video 有问题
            extension: _extname.length > 0 ? _extname : undefined,
            count: this.limitLength - this.files.length, //默认9
            onChooseFile: this.chooseFileCallback,
            onUploadProgress: (progressEvent) => {
              this.setProgress(progressEvent, progressEvent.index);
            },
          })
          .then((result) => {
            this.setSuccessAndError(result.tempFiles);
          })
          .catch((err) => {
            console.log('选择失败', err);
          });
    },

    /**
     * 选择文件回调
     * @param {Object} res
     */
    async chooseFileCallback(res) {
      const _extname = get_extname(this.fileExtname);
      const is_one =
          (Number(this.limitLength) === 1 && this.disablePreview && !this.disabled) ||
          this.returnType === 'object';
      // 如果这有一个文件 ，需要清空本地缓存数据
      if (is_one) {
        this.files = [];
      }

      let { filePaths, files } = get_files_and_is_max(res, _extname);
      if (!(_extname && _extname.length > 0)) {
        filePaths = res.tempFilePaths;
        files = res.tempFiles;
      }

      let currentData = [];
      for (let i = 0; i < files.length; i++) {
        if (this.limitLength - this.files.length <= 0) break;
        files[i].uuid = Date.now();
        let filedata = await get_file_data(files[i], this.fileMediatype);
        filedata.progress = 0;
        filedata.status = 'ready';
        this.files.push(filedata);
        currentData.push({
          ...filedata,
          file: files[i],
        });
      }
      this.$emit('select', {
        tempFiles: currentData,
        tempFilePaths: filePaths,
      });
      res.tempFiles = files;
      // 停止自动上传
      if (!this.autoUpload) {
        res.tempFiles = [];
      }
    },

    /**
     * 批传
     * @param {Object} e
     */
    uploadFiles(files) {
      files = [].concat(files);
      return uploadCloudFiles
          .call(this, files, 5, (res) => {
            this.setProgress(res, res.index, true);
          })
          .then((result) => {
            this.setSuccessAndError(result);
            return result;
          })
          .catch((err) => {
            console.log(err);
          });
    },

    /**
     * 成功或失败
     */
    async setSuccessAndError(res, fn) {
      let successData = [];
      let errorData = [];
      let tempFilePath = [];
      let errorTempFilePath = [];
      for (let i = 0; i < res.length; i++) {
        const item = res[i];
        const index = item.uuid ? this.files.findIndex((p) => p.uuid === item.uuid) : item.index;

        if (index === -1 || !this.files) break;
        if (item.errMsg === 'request:fail') {
          this.files[index].url = item.path;
          this.files[index].status = 'error';
          this.files[index].errMsg = item.errMsg;
          // this.files[index].progress = -1
          errorData.push(this.files[index]);
          errorTempFilePath.push(this.files[index].url);
        } else {
          this.files[index].errMsg = '';
          this.files[index].fileID = item.url;
          const reg = /cloud:\/\/([\w.]+\/?)\S*/;
          if (reg.test(item.url)) {
            this.files[index].url = await this.getTempFileURL(item.url);
          } else {
            this.files[index].url = item.url;
          }

          this.files[index].status = 'success';
          this.files[index].progress += 1;
          successData.push(this.files[index]);
          tempFilePath.push(this.files[index].fileID);
        }
      }

      if (successData.length > 0) {
        this.setEmit();
        // 状态改变返回
        this.$emit('success', {
          tempFiles: this.backObject(successData),
          tempFilePaths: tempFilePath,
        });
      }

      if (errorData.length > 0) {
        this.$emit('fail', {
          tempFiles: this.backObject(errorData),
          tempFilePaths: errorTempFilePath,
        });
      }
    },

    /**
     * 获取进度
     * @param {Object} progressEvent
     * @param {Object} index
     * @param {Object} type
     */
    setProgress(progressEvent, index, type) {
      const fileLenth = this.files.length;
      const percentNum = (index / fileLenth) * 100;
      const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
      let idx = index;
      if (!type) {
        idx = this.files.findIndex((p) => p.uuid === progressEvent.tempFile.uuid);
      }
      if (idx === -1 || !this.files[idx]) return;
      // fix by mehaotian 100 就会消失，-1 是为了让进度条消失
      this.files[idx].progress = percentCompleted - 1;
      // 上传中
      this.$emit('progress', {
        index: idx,
        progress: parseInt(percentCompleted),
        tempFile: this.files[idx],
      });
    },

    /**
     * 删除文件
     * @param {Object} index
     */
    delFile(index) {
      this.$emit('delete', {
        tempFile: this.files[index],
        tempFilePath: this.files[index]?.url,
      });
      console.log(this.files);
      this.files.splice(index, 1);
      console.log(this.files);
      this.$nextTick(() => {
        this.setEmit();
      });
    },

    /**
     * 获取文件名和后缀
     * @param {Object} name
     */
    getFileExt(name) {
      const last_len = name.lastIndexOf('.');
      const len = name.length;
      return {
        name: name.substring(0, last_len),
        ext: name.substring(last_len + 1, len),
      };
    },

    /**
     * 处理返回事件
     */
    setEmit() {
      let data = [];
      let updateUrl = [];
      if (this.returnType === 'object') {
        data = this.backObject(this.files)[0];
        this.localValue = data ? data : [];
        updateUrl = data ? data.url : [];
      } else {
        data = this.backObject(this.files);
        if (!this.localValue) {
          this.localValue = [];
        }
        this.localValue = [...data];
        if (this.localValue.length > 0) {
          this.localValue.forEach((item) => {
            updateUrl.push(item.url);
          });
        }
      }
      this.$emit('update:modelValue', this.localValue);
      this.$emit('update:url', updateUrl);
    },

    /**
     * 处理返回参数
     * @param {Object} files
     */
    backObject(files) {
      let newFilesData = [];
      files.forEach((v) => {
        newFilesData.push({
          extname: v.extname,
          fileType: v.fileType,
          image: v.image,
          name: v.name,
          path: v.path,
          size: v.size,
          fileID: v.fileID,
          url: v.url,
        });
      });
      return newFilesData;
    },
    async getTempFileURL(fileList) {
      fileList = {
        fileList: [].concat(fileList),
      };
      const urls = await uniCloud.getTempFileURL(fileList);
      return urls.fileList[0].tempFileURL || '';
    },
    /**
     * 获取父元素实例
     */
    getForm(name = 'uniForms') {
      let parent = this.$parent;
      let parentName = parent.$options.name;
      while (parentName !== name) {
        parent = parent.$parent;
        if (!parent) return false;
        parentName = parent.$options.name;
      }
      return parent;
    },
  },
};
</script>

<style lang="scss" scoped>
.uni-file-picker {
  /* #ifndef APP-NVUE */
  box-sizing: border-box;
  overflow: hidden;
  /* width: 100%; */
  /* #endif */
  /* flex: 1; */
  position: relative;
}

.uni-file-picker__header {
  padding-top: 5px;
  padding-bottom: 10px;
  /* #ifndef APP-NVUE */
  display: flex;
  /* #endif */
  justify-content: space-between;
}

.file-title {
  font-size: 14px;
  color: #333;
}

.file-count {
  font-size: 14px;
  color: #999;
}

.is-add {
  /* #ifndef APP-NVUE */
  display: flex;
  /* #endif */
  align-items: center;
  justify-content: center;
}
.add-icon {
  width: 57rpx;
  height: 49rpx;
}
.file-subtitle {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  bottom: 0;
  width: 140rpx;
  height: 36rpx;
  z-index: 1;
  display: flex;
  justify-content: center;
  color: #fff;
  font-weight: 500;
  background: rgba(#000, 0.3);
  font-size: 24rpx;
}
</style>
